package org.hackystat.sdsa.analyzer.model.activity.edit;

import java.util.Map;
import java.util.logging.Logger;

import jess.Fact;
import jess.JessException;
import jess.RU;
import jess.Rete;
import jess.Value;

import org.hackystat.sdsa.analyzer.control.SensorDataUtil;
import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData;

/**
 * Implements unit test edit activity.
 * 
 * @author Hongbing Kou
 */
public class UnitTestEditActivity extends ProductionEditActivity {
  /** Number of test methods. */
  private int numOfTestMethods = 0;
  /** Number of test assertions. */
  private int numOfTestAssertions = 0;
  /** Number of test method changes. */
  private int testMethodIncrease = 0;
  /** Number of test assertion change. */
  private int testAssertionIncrease = 0;
  /** Number of methods in test class. */
  
  /** Number of current test methods.*/
  public static final String CURRENT_TEST_METHODS = "current-test-methods";
  /** Number of current test statements. */
  public static final String CURRENT_TEST_ASSERTIONS = "current-test-assertions";
  /** Test edit type. */
  private static final String TYPE = "TEST EDIT";
  
  /**
   * Empty constructor.
   */
  public UnitTestEditActivity() {
    super.setType(TYPE);
  }
  
  /**
   * Constructs an edit action on a file.
   * 
   * @param sensorData Sensor data.
   * @param logger Logger.
   */
  public UnitTestEditActivity(SensorData sensorData, Logger logger) {
    super(sensorData, logger);
    super.setType(TYPE);
    
    Map<String, String> propMap = SensorDataUtil.propMap(sensorData);
    String numOfTestMethods = propMap.get(CURRENT_TEST_METHODS);
    this.numOfTestMethods = Integer.parseInt(numOfTestMethods);

    String numOfTestAssertions = propMap.get(CURRENT_TEST_ASSERTIONS);
    this.numOfTestAssertions = Integer.parseInt(numOfTestAssertions);
  }
  
  /**
   * Makes Jess facts in the given Jess rete engine.
   * 
   * @param index Action index in episode.
   * @param engine Jess rete engine.
   * @throws JessException If error while constructing jess action.
   * @return The asserted fact.
   */
  public Fact assertAsFact(int index, Rete engine) throws JessException  {
    Fact assertedFact = null;
    if (isSubstantial()) {
      Fact f = new Fact("UnitTestEditActivity", engine);
      f.setSlotValue(INDEX_SLOT, new Value(index, RU.INTEGER));
      String file = super.extractFileName(super.getResourceUri());
      f.setSlotValue(FILE_SLOT, new Value(file, RU.STRING));
      f.setSlotValue("duration", new Value(this.getDuration(), RU.INTEGER));
      f.setSlotValue("testChange", new Value(this.getTestMethodIncrease(), RU.INTEGER));
      f.setSlotValue("assertionChange", new Value(this.getTestAssertionIncrease(), RU.INTEGER));
      f.setSlotValue("byteChange", new Value(this.getFileSizeIncrease(), RU.INTEGER));

      assertedFact = engine.assertFact(f);
    }
    
    return assertedFact;
  }

  /**
   * Sets number of test methods to the edited test file.
   * 
   * @param value
   *          Number of test methods.
   */
  public void setNumOfTestMethods(int value) {
    this.numOfTestMethods = value;    
  }
  
  /**
   * Sets number of test assertions to the edited file.
   * 
   * @param value
   *          Number of test assertions.
   */
  public void setNumOfTestAssertions(int value) {
    this.numOfTestAssertions = value;
  }

  /**
   * Gets number of test methods.
   * 
   * @return Number of test methods.
   */
  public int getNumOfTestMethods() {
    return this.numOfTestMethods;
  }
  
  /**
   * Gets number of test assertions.
   * 
   * @return Number of test assertions.
   */
  public int getNumOfTestAssertions() {
    return this.numOfTestAssertions;
  }

  /**
   * Sets number of test method changes.
   * 
   * @param value Number of method changes.
   */
  public void setTestMethodIncrease(int value) {
    this.testMethodIncrease = value;
  }
  
  /**
   * Gets the number of test method change.
   * 
   * @return Number of test method change.
   */
  public int getTestMethodIncrease() {
    return this.testMethodIncrease;
  }
  
  /**
   * Sets number of test assertion change.
   * 
   * @param value
   *          Number of test assertions change.
   */
  public void setTestAssertionIncrease(int value) {
    this.testAssertionIncrease = value;
  }
  
  /**
   * Gets number of test assertion change.
   * 
   * @return Number of test assertion changes.
   */
  public int getTestAssertionIncrease() {
    return this.testAssertionIncrease;
  }
  
  /**
   * Gets unit test edit action string.
   * 
   * @return Unit test edit action string.
   */
  @Override public String toString() {
    StringBuffer buf = new StringBuffer();
    buf.append(super.toString());
    buf.append(" TEST {");
    
    // Method increase
    buf.append(makeMetricPair("MI", this.getMethodIncrease(), this.getNumOfMethods())).append(", ");
    // Statement increase
    buf.append(makeMetricPair("SI", this.getStatementIncrease(), this.getNumOfStatements()))
       .append(", ");
    
    // Test method increase
    buf.append(makeMetricPair("TI", this.testMethodIncrease, this.numOfTestMethods)).append(", ");
    // Assertion increase
    buf.append(makeMetricPair("AI", this.testAssertionIncrease, this.numOfTestAssertions));
    buf.append(", ");
    // File size change.
    buf.append(super.makeMetricPair("FI", this.fileSizeIncrease, this.fileSize));

    buf.append('}');    
    return  buf.toString();
  }

  /**
   * Checks unit test code edit.
   * 
   * @return True if edit time is positive.
   */
  @Override public boolean hasProgress() {
    return this.getDuration() > 0 || this.getFileSizeIncrease() != 0;
  }
  

  /**
   * Checks whether this edit work is significant or not.
   * 
   * @return True if the edit is substantial and false otherwise.
   */
  @Override public boolean isSubstantial() {
//    return this.getDuration() > 0 && 
//          (this.methodIncrease != 0 || this.statementIncrease != 0 || 
//           this.testMethodIncrease != 0 || this.testAssertionIncrease != 0);  
  return this.getMethodIncrease() != 0 || this.getStatementIncrease() != 0 || 
         this.testMethodIncrease != 0 || this.testAssertionIncrease != 0;  
  }

  /** 
   * Get the representation of this activity. 
   * 
   * @return String representation of this activity.
   */
  @Override public String getRepresentation() {
    StringBuffer buf = new StringBuffer();
    buf.append(super.getDuration()).append("sec ");
    
    // Method increase
    buf.append(makeMetricPair("MI", this.getMethodIncrease(), this.getNumOfMethods())).append(", ");
    // Statement increase
    buf.append(makeMetricPair("SI", this.getStatementIncrease(), this.getNumOfStatements()))
       .append(", ");
    // Test method increase
    buf.append(makeMetricPair("TI", this.testMethodIncrease, this.numOfTestMethods)).append(", ");
    // Assertion increase
    buf.append(makeMetricPair("AI", this.testAssertionIncrease, this.numOfTestAssertions));
    buf.append(", ");
    // File size change.
    buf.append(super.makeMetricPair("FI", this.fileSizeIncrease, this.fileSize));
  
    return buf.toString();
  }
}

